﻿using System;
using System.Data;

namespace CommonUtils.CodeFactory
{
    public static class Identifier
    {
        public static string GetFromTypeName(string name, bool nullable)
        {
            switch (name)
            {
                case "Guid": return "Guid";
                case "String": return "string";
                case "Boolean": return nullable ? "bool?" : "bool";
                case "Int16": return nullable ? "short?" : "short";
                case "Int32": return nullable ? "int?" : "int";
                case "Int64": return nullable ? "long?" : "long";
                case "UInt64": return nullable ? "ulong?" : "ulong";
                case "Single": return nullable ? "float?" : "float";
                case "Double": return nullable ? "double?" : "double";
                case "Decimal": return nullable ? "decimal?" : "decimal";
                case "Byte": return nullable ? "byte?" : "byte";
                case "DateTime": return nullable ? "DateTime?" : "DateTime";

                case "String[]": return "string[]";
                case "Int16[]": return nullable ? "short?[]" : "short[]";
                case "Int32[]": return nullable ? "int?[]" : "int[]";
                case "Int64[]": return nullable ? "long?[]" : "long[]";
                case "Single[]": return nullable ? "float?[]" : "float[]";
                case "Double[]": return nullable ? "double?[]" : "double[]";
                case "Decimal[]": return nullable ? "decimal?[]" : "decimal[]";
                case "Byte[]": return nullable ? "byte?[]" : "byte[]";

                default: throw new ExceptionPlus("Identifier Undefined: " + name);
            }
        }

        public static string GetIdentifier(this Type type, bool nullable)
        => GetFromTypeName(type.Name, nullable);

        public static string GetIdentifier(this DbType type, bool nullable)
        => GetFromTypeName(type.ToString(), nullable);

        public static Type GetTypeByDbIdentifier(string identifier)
        {
            switch (identifier.ToLower())
            {
                case "enum":
                case "varchar":
                case "text":
                    return typeof(string);
                case "bit":
                    return typeof(bool);
                case "int":
                    return typeof(int);
                case "bigint":
                    return typeof(long);
                case "double":
                    return typeof(double);
                case "decimal":
                    return typeof(decimal);
                case "blob":
                case "binary":
                    return typeof(byte[]);
                case "datetime":
                case "timestamp":
                    return typeof(DateTime);
                default: throw new ExceptionPlus("Type Undefined: " + identifier);
            }
        }

        public static string GetFromDbDataTypeIdentifier(string identifier, bool nullable)
        => GetTypeByDbIdentifier(identifier).GetIdentifier(nullable);

        public static string GetDbIdentifier(this Type type)
        {
            if (type.IsArray)
                return "text";

            if (type.IsEnum)
                return "int";

            var mark = type.ToString();
            switch (mark)
            {
                case "System.String":
                    return "text";

                case "System.Boolean":
                    return "blob not null";

                case "System.Nullable`1[System.Boolean]":
                    return "blob";

                case "System.Int32":
                    return "int not null";

                case "System.Nullable`1[System.Int32]":
                    return "int";

                case "System.Int16":
                    return "smallint not null";

                case "System.Nullable`1[System.Int16]":
                    return "smallint";

                case "System.Int64":
                    return "bigint not null";

                case "System.Nullable`1[System.Int64]":
                    return "bigint";

                case "System.Single":
                    return "float not null";

                case "System.Nullable`1[System.Single]":
                    return "float";

                case "System.Double":
                    return "double not null";

                case "System.Nullable`1[System.Double]":
                    return "double";

                case "System.Decimal":
                    return "decimal not null";

                case "System.Nullable`1[System.Decimal]":
                    return "decimal";

                case "System.Byte":
                    return "binary not null";

                case "System.Nullable`1[System.Byte]":
                    return "binary";

                case "System.DateTime":
                    return "dateTime not null";

                case "System.Nullable`1[System.DateTime]":
                    return "dateTime";

                default:
                    throw new ExceptionPlus("DbDataTypeIdentifier Undifined: " + type.FullName);
            }
        }

        public static string FieldIdentified(this string field)
        {
            field = field.Replace(StringIdentifier.Blank, StringIdentifier.BlankField);
            if (field.StartsWith(StringIdentifier.DigitChars))
                field = '@' + field;
            return field;
        }

        public static string FieldRevert(this string field)
        => field.Replace(StringIdentifier.BlankField, StringIdentifier.Blank).Remove('@');

        public static string GetDateTypeIdentifier(this ColumnSchema columnSchema)
        => columnSchema.DataType.GetIdentifier(columnSchema.Nullable);

        public static string GetFieldName(this ColumnSchema columnSchema)
        => columnSchema.Name.FieldIdentified();
    }
}
